package top.codedance.iotp.client.plugins.async.udp;

import cd.io.netty.bootstrap.Bootstrap;
import cd.io.netty.buffer.ByteBuf;
import cd.io.netty.buffer.Unpooled;
import cd.io.netty.channel.*;
import cd.io.netty.channel.nio.NioEventLoopGroup;
import cd.io.netty.channel.socket.DatagramChannel;
import cd.io.netty.channel.socket.DatagramPacket;
import cd.io.netty.channel.socket.nio.NioDatagramChannel;
import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.client.ClientApplication;
import top.codedance.iotp.common.executer.AsyncAbstractExecuter;
import top.codedance.iotp.common.entity.CommonEntity;
import org.apache.log4j.Logger;

import javax.xml.bind.DatatypeConverter;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;

public class UdpService extends Service {
    private Logger logger = ClientApplication.getLogger(UdpService.class);
    private ChannelFuture channelFuture = null;
    private Integer instance;

    public UdpService(AsyncAbstractExecuter executer, UdpServerParam param, Integer instance) {
        super(executer, param);
        this.instance = instance;
    }

    public synchronized void createConnect() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(new ChannelInitializer<DatagramChannel>() {
                        @Override
                        protected void initChannel(DatagramChannel datagramChannel) throws Exception {
                            datagramChannel.pipeline().addLast(new SimpleChannelInboundHandler<DatagramPacket>(){
                                @Override
                                protected void messageReceived(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket) throws Exception {
                                    InetSocketAddress sender = datagramPacket.sender();
                                    String ip = sender.getAddress().getHostAddress();
                                    logger.debug("Reqeust IP: " + ip);
                                    ByteBuf buf = datagramPacket.copy().content();
                                    try {
                                        byte[] data = new byte[buf.readableBytes()];
                                        buf.readBytes(data);
                                        receipt(DatatypeConverter.printHexBinary(data));
                                    }catch (Exception e){
                                        e.printStackTrace();
                                    }

                                }
                            });
                        }
                    });
            channelFuture = bootstrap.bind(0).sync();
            UdpExecuterImpl.udps.put(instance, this);
        } catch (Exception e) {
            logger.debug(e.getMessage());
        }
        logger.debug("Async com client is running!");
    }

    public void close(){
        try {
            channelFuture.channel().closeFuture().await();
            UdpExecuterImpl.udps.remove(instance);
        }catch (Exception e){
            logger.debug(e.getMessage());
        }
    }

    @Override
    public void receipt(String body) {
        synchronized (UdpExecuterImpl.class) {
            Set<Map.Entry<Integer, Set<String>>> entries = UdpExecuterImpl.tasks.entrySet();
            for (Map.Entry<Integer, Set<String>> entry : entries){
                if(entry.getKey() == instance){
                    CommonEntity ce = genCommonEntity();
                    ce.setBody(body.getBytes(StandardCharsets.UTF_8));
                    Set<String> value = entry.getValue();
                    for (String cid : value){
                        executer.send(ce.build(), cid);
                        logger.debug("Send data to " + cid);
                    }
                    break;
                }
            }
        }
    }

    @Override
    public void send(String command) {
        try {
            if(channelFuture == null || !channelFuture.channel().isOpen() || !channelFuture.channel().isActive()) {
                createConnect();
            }
            if(channelFuture != null && channelFuture.channel().isOpen() && channelFuture.channel().isActive()) {
                channelFuture.channel().writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(command, StandardCharsets.UTF_8), new InetSocketAddress(param.getIp(), param.getPort())));
            }
        }catch (Exception e){
            logger.debug(e.getMessage());
        }
    }

    @Override
    public void send(byte[] bytes) {
        try {
            if(channelFuture == null || !channelFuture.channel().isOpen() || !channelFuture.channel().isActive()) {
                createConnect();
            }
            if(channelFuture != null && channelFuture.channel().isOpen() && channelFuture.channel().isActive()) {
                channelFuture.channel().writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(bytes), new InetSocketAddress(param.getIp(), param.getPort())));
            }
        }catch (Exception e){
            logger.debug(e.getMessage());
        }
    }
}
